/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package dljk.core;

import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;

enum BoardField {
    BF_CLOSED_CARDS, BF_OPEN_CARDS, BF_FINISH_1, BF_FINISH_2,
    BF_FINISH_3, BF_FINISH_4, BF_GAMEZONE_1, BF_GAMEZONE_2,
    BF_GAMEZONE_3, BF_GAMEZONE_4, BF_GAMEZONE_5, BF_GAMEZONE_6,
    BF_GAMEZONE_7
};

/**
 *
 * @author Ariman
 */
public class KlondikeEngine {

    public KlondikeEngine() {
        klondikeCardSet = new EnumMap<BoardField, Deck>(BoardField.class);
        Deck tmpDeck;
        Deck startDeck = new Deck();
        startDeck.createSolitareDeck();
        startDeck.shuffle();

        GameCard card;
        for (BoardField bf : BoardField.values()) {
            switch (bf) {
                case BF_CLOSED_CARDS:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 19; i++) {
                        tmpDeck.add(startDeck.removeTop());
                    }
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_OPEN_CARDS:
                case BF_FINISH_1:
                case BF_FINISH_2:
                case BF_FINISH_3:
                case BF_FINISH_4:
                    klondikeCardSet.put(bf, new Deck());
                    break;

                case BF_GAMEZONE_1:
                    tmpDeck = new Deck();
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_2:
                    tmpDeck = new Deck();
                    card = startDeck.removeTop();
                    card.setFace(false);
                    tmpDeck.add(card);
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_3:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 2; i++) {
                        card = startDeck.removeTop();
                        card.setFace(false);
                        tmpDeck.add(card);
                    }
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_4:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 3; i++) {
                        card = startDeck.removeTop();
                        card.setFace(false);
                        tmpDeck.add(card);
                    }
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_5:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 4; i++) {
                        card = startDeck.removeTop();
                        card.setFace(false);
                        tmpDeck.add(card);
                    }
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_6:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 5; i++) {
                        card = startDeck.removeTop();
                        card.setFace(false);
                        tmpDeck.add(card);
                    }
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;

                case BF_GAMEZONE_7:
                    tmpDeck = new Deck();
                    for (int i = 0; i < 6; i++) {
                        card = startDeck.removeTop();
                        card.setFace(false);
                        tmpDeck.add(card);
                    }
                    card = startDeck.removeTop();
                    card.setFace(true);
                    tmpDeck.add(card);
                    klondikeCardSet.put(bf, tmpDeck);
                    break;
            }
        }
    }

    public void OnFieldClicked(Pair pair) {
        if (pair.getBoardField() == null) {
            return;
        }
        BoardField clickedField = pair.getBoardField();
        int clickedCardIndex = pair.getCardNumber();
        BoardField selectedField = GetSelectedField();
        int selectedCardIndex = GetSelectedCardIndex();

        switch (clickedField) {
            case BF_GAMEZONE_1:
            case BF_GAMEZONE_2:
            case BF_GAMEZONE_3:
            case BF_GAMEZONE_4:
            case BF_GAMEZONE_5:
            case BF_GAMEZONE_6:
            case BF_GAMEZONE_7:
                if (selectedField == null) {
                    // кликнуто игровое поле. нет выбранных карт
                    // клик мимо
                    if (clickedCardIndex == -1) {
                        return;
                    }
                    // если кликнутая карта закрыта
                    // если она последняя, то переворачиваем ее
                    if (!klondikeCardSet.get(clickedField).get(clickedCardIndex).getFace()) {
                        if (klondikeCardSet.get(clickedField).size() - 1 == clickedCardIndex) {
                            klondikeCardSet.get(clickedField).get(clickedCardIndex).setFace(true);
                        }
                        return;
                    }
                    // выделяем от кликнутой карты до конца
                    for (int i = clickedCardIndex; i < klondikeCardSet.get(clickedField).size(); i++) {
                        klondikeCardSet.get(clickedField).get(i).setSelected(true);
                    }
                    return;
                } else {
                    // кликнуто игровое поле. есть выбранные карты
                    // если выбранные карты в кликнутом поле, то снимаем выделение
                    if (clickedField == selectedField) {
                        for (GameCard gc : klondikeCardSet.get(clickedField)) {
                            gc.setSelected(false);
                        }
                        return;
                    }
                    // клик по другому полю в игровой области
                    if (klondikeCardSet.get(clickedField).isEmpty()) {
                        // на пустое место можно положить только короля
                        if (klondikeCardSet.get(selectedField).get(selectedCardIndex).getPriority() != 13) return;
                        MoveCards(clickedField);
                    } else {
                        // в поле уже есть карты
                        GameCard dstCard = klondikeCardSet.get(clickedField).get(klondikeCardSet.get(clickedField).size()-1);
                        GameCard srcCard = klondikeCardSet.get(selectedField).get(selectedCardIndex);
                        // открыта последняя карта?
                        if (!dstCard.getFace()) return;
                        // меньшую на большую разноцветной масти
                        boolean srcSuiteColor = false;
                        boolean dstSuiteColor = false;
                        if ((dstCard.getSuite() == CardSuites.DIAMONDS) || (dstCard.getSuite() == CardSuites.HEARTS)) dstSuiteColor = true;
                        if ((srcCard.getSuite() == CardSuites.DIAMONDS) || (srcCard.getSuite() == CardSuites.HEARTS)) srcSuiteColor = true;
                        if (dstSuiteColor == srcSuiteColor) return;
                        if (dstCard.getPriority()-1 != srcCard.getPriority()) return;
                        MoveCards(clickedField);
                    }
                }
                break;

            case BF_FINISH_1:
            case BF_FINISH_2:
            case BF_FINISH_3:
            case BF_FINISH_4:
                // если нет выбранных карт выходим
                if ((selectedField == null) || (selectedCardIndex == -1)) return;
                // выделена только последняя карта?
                if (klondikeCardSet.get(selectedField).size()-1 != selectedCardIndex) return;
                if (klondikeCardSet.get(clickedField).size() == 0) {
                    // финальное поле пусто, можно положить только туза
                    if (klondikeCardSet.get(selectedField).get(selectedCardIndex).getPriority() == 1)
                        MoveCards(clickedField);
                } else {
                    // в финальном поле уже есть карты
                    // ложим большую на меньшую, одной масти
                    GameCard srcCard = klondikeCardSet.get(selectedField).get(selectedCardIndex);
                    GameCard dstCard = klondikeCardSet.get(clickedField).get(klondikeCardSet.get(clickedField).size()-1);
                    if ((srcCard.getPriority()-1 == dstCard.getPriority()) && (srcCard.getSuite()==dstCard.getSuite()))
                        MoveCards(clickedField);
                }
                break;
                
            case BF_CLOSED_CARDS:
                // деселектим все карты в открытом поле
                for (GameCard gc : klondikeCardSet.get(BoardField.BF_OPEN_CARDS)) gc.setSelected(false);
                if (klondikeCardSet.get(clickedField).isEmpty()) {
                    // переворачиваем пролистанную колоду назад
                    if (klondikeCardSet.get(BoardField.BF_OPEN_CARDS).isEmpty()) return;
                    GameCard gc;
                    while ((gc=klondikeCardSet.get(BoardField.BF_OPEN_CARDS).removeTop())!=null) {
                        gc.setFace(false);
                        klondikeCardSet.get(BoardField.BF_CLOSED_CARDS).add(gc);
                    }
                } else {
                    // достаем 1 карту и переворачиваем
                    GameCard gc = klondikeCardSet.get(BoardField.BF_CLOSED_CARDS).removeTop();
                    gc.setFace(true);
                    klondikeCardSet.get(BoardField.BF_OPEN_CARDS).add(gc);
                }
                break;
                
            case BF_OPEN_CARDS:
                // есть выбранные карты?
                if ((selectedField != null) && (selectedField != clickedField)) return;
                // есть карты в этом поле?
                if (klondikeCardSet.get(BoardField.BF_OPEN_CARDS).isEmpty()) return;
                // выбираем последнюю карту
                if (selectedField == clickedField)
                    klondikeCardSet.get(BoardField.BF_OPEN_CARDS).get(klondikeCardSet.get(BoardField.BF_OPEN_CARDS).size()-1).setSelected(false);
                else
                    klondikeCardSet.get(BoardField.BF_OPEN_CARDS).get(klondikeCardSet.get(BoardField.BF_OPEN_CARDS).size()-1).setSelected(true);
                break;
        }
    }

    private void MoveCards(BoardField bfDst) {
        Deck tmpDeck = new Deck();
        BoardField selectedField = GetSelectedField();
        int selectedCardIndex = GetSelectedCardIndex();

        int moveCardCount = klondikeCardSet.get(selectedField).size() - selectedCardIndex;
        for (int i = 0; i < moveCardCount; i++) {
            tmpDeck.add(klondikeCardSet.get(selectedField).removeTop());
            tmpDeck.get(tmpDeck.size() - 1).setSelected(false);
        }

        GameCard gc;
        while ((gc = tmpDeck.removeTop()) != null) {
            klondikeCardSet.get(bfDst).add(gc);
        }
    }

    public Map<BoardField, Deck> getKlondikeCardSet() {
        return Collections.unmodifiableMap(klondikeCardSet);
    }

    private int GetSelectedCardIndex() {
        BoardField bfSelected = GetSelectedField();
        if (bfSelected == null) {
            return -1;
        }

        for (GameCard gc : klondikeCardSet.get(bfSelected)) {
            if (gc.getSelected()) {
                return klondikeCardSet.get(bfSelected).indexOf(gc);
            }
        }

        return -1;
    }

    // возвращает поле, в котором выделенная карта, либо null
    private BoardField GetSelectedField() {
        for (BoardField bf : BoardField.values()) {
            for (GameCard gc : klondikeCardSet.get(bf)) {
                if (gc.getSelected()) {
                    return bf;
                }
            }
        }

        return null;
    }
    private EnumMap<BoardField, Deck> klondikeCardSet;
}
